/**
 * Metro configuration for React Native
 * https://github.com/facebook/react-native
 *
 * @format
 */
const fs = require("fs");
const pathUtil = require("path");
const commonUtil = require("./metro.config.utils");
const defaultCreateModuleIdFactory = require("./node_modules/metro/src/lib/createModuleIdFactory");

const BUILD_TYPE_COMMOM = "common";
const BUILD_TYPE_DEFAULT = "default";
const BUILD_TYPE_DIFF = "diff";

const projectRootPath = `${process.cwd()}`;
let moduleIdMaps = new Map();
var entryFilePath = "";

var recordAllModulesDependenceSet = new Set(); // 记录所有依赖的 ModuleId
var recordExcludeCommonModulesDependenceSet = new Set(); // 记录所有不在common.js中的 ModuleId

/**
 * Create a Id with local storage.
 */
buildCreateModuleIdFactoryWithLocalStorage = function(buildConfig) {
  if (buildConfig.type == BUILD_TYPE_DEFAULT) {
    // Return the orginal createModuleIdFactory implements.
    // Please. See node_modules / metro / src / lib / createModuleIdFactory.js
    return defaultCreateModuleIdFactory;
  } else {
    // Init the local map from remote server to get the ModuleId quickly.
    var resp = commonUtil.getAllModuleIdsSync();
    if (resp.data instanceof Array) {
      resp.data.forEach(element => {
        moduleIdMaps.set(element.findKey, element);
      });
    }
    return () => {
      return path => {
        let relativePath = commonUtil.getFileRelativePath(
          projectRootPath,
          path
        );
        //FIXME
        if (
          (relativePath.startsWith("index.") && relativePath.endsWith(".js")) ||
          relativePath.startsWith("common")
        ) {
          entryFilePath = relativePath;
        }
        //
        let fileStatus = fs.statSync(path);
        let hashInHex = commonUtil.getFileHashInHex(path, "sha256");
        let findKey = commonUtil.buildFindKey(relativePath, hashInHex);

        if (!moduleIdMaps.has(findKey)) {
          let size = fileStatus.size; // 文件的大小
          let hashType = "sha256";

          let resp = commonUtil.createModuleIdSync(
            findKey,
            relativePath,
            hashInHex,
            hashType,
            size,
            buildConfig.type
          );
          // Fixme 判断报文是否正常
          let element = resp.data;
          moduleIdMaps.set(findKey, element);
          console.log(
            `get ModuleId From Global ModuleId Service: ${element.id} for ${relativePath}`
          );
        }
        let ret = moduleIdMaps.get(findKey);
        recordAllModulesDependenceSet.add(ret.id);
        if (ret.type !== BUILD_TYPE_COMMOM) {
          recordExcludeCommonModulesDependenceSet.add(ret.id);
        }
        return ret.id;
      };
    };
  }
};

function saveJsModuleCodeToFile(outputDir, moduleId, code) {
  let depFilePath = pathUtil.join(outputDir, `${moduleId}.js`);
  commonUtil.saveDataToFile(depFilePath, code);
  console.log(`Saved ${pathUtil.basename(depFilePath)}`);
}

buildProcessModuleFilter = function(buildConfig) {
  return moduleObj => {
    saveBundleReportToFile(buildConfig.outputDir);
    let path = moduleObj.path;
    // handle modules without localfile.
    if (!fs.existsSync(path)) {
      return true;
    }

    let relativePath = commonUtil.getFileRelativePath(projectRootPath, path);
    let hashInHex = commonUtil.getFileHashInHex(path, "sha256");
    let findKey = commonUtil.buildFindKey(relativePath, hashInHex);
    let storeObj = moduleIdMaps.get(findKey);

    if (storeObj != null) {
      // saveJsModuleCodeToFile(
      //   pathUtil.join(buildConfig.outputDir, "js_modules"),
      //   storeObj.id,
      //   moduleObj.output[0].data.code
      // );
    }

    if (buildConfig.type == BUILD_TYPE_DIFF) {
      if (storeObj != null && storeObj.type == BUILD_TYPE_COMMOM) {
        return false;
      }
      return true;
    }
    return true;
  };
};

var isBundleFileSaved = false;
saveBundleReportToFile = function(saveDir) {
  if (isBundleFileSaved) {
    return;
  }
  let depFilePath = pathUtil.join(saveDir, `report.${entryFilePath}.json`);
  let tmp = {
    timestamp: commonUtil.getCurrentTimestamp(),
    entryFilePath: entryFilePath,
    dependencies: Array.from(recordAllModulesDependenceSet),
    dependenciesWithoutCommon: Array.from(
      recordExcludeCommonModulesDependenceSet
    ),
    dependenciesSize: recordAllModulesDependenceSet.size,
    dependenciesWithoutCommonSize: recordExcludeCommonModulesDependenceSet.size
  };
  let data = JSON.stringify(tmp);
  commonUtil.saveDataToFile(depFilePath, data);
};

module.exports = {
  BuildType: {
    COMMON: BUILD_TYPE_COMMOM,
    DEFAULT: BUILD_TYPE_DEFAULT,
    DIFF: BUILD_TYPE_DIFF
  },
  buildCreateModuleIdFactory: buildCreateModuleIdFactoryWithLocalStorage,
  buildProcessModuleFilter: buildProcessModuleFilter
};
